using System;
using System.Net;
using System.Net.Sockets;

namespace SharpMud.Net
{
	public class ConnectionListener : IConnectionListener, System.Threading.IRequiresExternalPulsing
	{
		#region Private Variables
		private System.Collections.ArrayList		_WaitingConnections;
		private System.Net.Sockets.TcpListener		_TcpListener;
		private int									_Port;
		private System.Net.IPAddress				_Address;
		private Server.Systems.Net.INetworkSystem	_Host;
        private log4net.ILog                        log;
		#endregion
		
		#region Constructors
		/// <summary>
		/// Default constructor. Creates a new, null instance of ConnectionListener.
		/// </summary>
		private ConnectionListener()
		{
			_WaitingConnections = new System.Collections.ArrayList();
			_Port = 0;
			_Address = System.Net.IPAddress.None;
			_Host = null;
		}
		/// <summary>
		/// A public constructor for ConnectionListener. Give it the port and the default handler and
		/// the connection listner will listen on all local address bindings at that port.
		/// </summary>
		/// <param name="port">The port to listen on</param>
		/// <param name="defaultHandler">The handler that will accept all the new connections generated
		/// from the listener.</param>
		public ConnectionListener(int port, Server.Systems.Net.INetworkSystem host)
			:this(System.Net.IPAddress.Any,port,host)
		{
		}
		/// <summary>
		/// A public constructor for ConnectionListener. Give it the local listening address and the local
		/// port to listen on, and a handler for new connections made at that endpoint.
		/// </summary>
		/// <param name="address">The local address to listen on</param>
		/// <param name="port">The port to listen on</param>
		/// <param name="defaultHandler">The handler that will accpet all the new connections generated from the listener</param>
		public ConnectionListener(System.Net.IPAddress address, int port,Server.Systems.Net.INetworkSystem host)
		{
			if(address==null)
				throw new ArgumentNullException("address");
			if(port<1024)
				throw new ArgumentOutOfRangeException("port",port,"The port of a ConnectionListener cannot be less than 1024.");
			if(host==null)
				throw new ArgumentNullException("host");

			_Port = port;
			_Address = address;
			_WaitingConnections = new System.Collections.ArrayList();
			_TcpListener = new System.Net.Sockets.TcpListener(_Address, _Port);
			_Host = host;

            log = log4net.LogManager.GetLogger(this.GetType().Name + "[localhost:" + _Port.ToString() + "]");
            log.Debug("CREATED");
		}
		#endregion

		/// <summary>
		/// This property returns the hosting network system of this ConnectionListener
		/// </summary>
		public SharpMud.Server.Systems.Net.INetworkSystem Host
		{
			get
			{
				return this._Host;		
			}
		}
	
		#region IConnectionListener Members

		/// <summary>
		/// The list of connections that have had their sockets accepted and are awaiting to be handled
		/// </summary>
		public INetworkConnection[] WaitingConnections
		{
			get
			{
				return (INetworkConnection[])_WaitingConnections.ToArray(typeof(INetworkConnection));
			}
		}

		/// <summary>
		/// The local port that is being listened on
		/// </summary>
		public int Port
		{
			get
			{
				return this._Port;
			}
		}

		/// <summary>
		/// The local endpoint that is being listened on
		/// </summary>
		public System.Net.IPEndPoint ListenPoint
		{
			get
			{
				return (IPEndPoint)this._TcpListener.LocalEndpoint;
			}
		}

		/// <summary>
		/// Removes a connection from the list of new connections that are waiting to be handled
		/// </summary>
		/// <param name="connection">The connection to remove</param>
		public void RemoveWaitingConnection(INetworkConnection connection)
		{
			this._WaitingConnections.Remove(connection);
		}

		private event SharpMud.Net.ConnectionListenerEventHandler _ConnectionMade;
		/// <summary>
		/// This event is raised when a connection is made
		/// </summary>
		public event SharpMud.Net.ConnectionListenerEventHandler ConnectionMade
		{
			add
			{
				_ConnectionMade += value;
			}
			remove
			{
				_ConnectionMade -= value;
			}
		}

		/// <summary>
		/// Tell the ConnectionListener to do any work that it needs to be told to do, such as check for new c
		/// </summary>
		public void Pulse()
		{
			lock(this)
			{
				bool stillLoop=true;
				try
				{
					stillLoop = _TcpListener.Pending();
				}
				catch(System.InvalidOperationException e)
				{
					stillLoop = false;
				}
				while(stillLoop)
				{
					NetworkConnection cw = new NetworkConnection(_TcpListener.AcceptSocket());

					this._WaitingConnections.Add(cw);
				
					if(this._ConnectionMade!=null)
						this._ConnectionMade(this,cw);

					try
					{
						stillLoop = _TcpListener.Pending();
					}
					catch(System.InvalidOperationException e)
					{
						stillLoop = false;
					}
				}
			}
		}

		/// <summary>
		/// Tell the ConnectionListener to start listening for new connections
		/// </summary>
		public void Start()
		{
            log.Debug("Starting");
			this._TcpListener.Start();
            log.Info("STARTED");
		}

		/// <summary>
		/// Tell the ConnectionListener to stop listening for new connections
		/// </summary>
		public void Stop()
		{
            log.Debug("Stopping");
			this._TcpListener.Stop();
            log.Info("STOPPED");
		}

		#endregion

	}
}
